// Effective C++ 条款32：确定你的public继承塑模出is-a关系
// 条款32的核心是：public继承在C++中表达的是"is-a"（是一种）关系。这是面向对象设计中最基本也是最重要的原则之一。
// "is-a"关系意味着派生类对象也是一个基类对象，派生类的每个实例都可以被视为基类的实例。这不仅是语法上的关系，更是语义上的承诺。

// is-a关系的数学表达
// 从集合论的角度看，如果B公有继承自A，则意味着：
//      B的集合是A的集合的子集
//      所有适用于A的性质也适用于B
//      B可能有A没有的额外性质

// 经典示例与误用
// 正确的is-a关系
class Animal
{
public:
    virtual void eat() = 0;
    virtual void sleep() = 0;
};

class Bird : public Animal
{ // Bird is-an Animal
public:
    void eat() override { /* 鸟类进食行为 */ }
    void sleep() override { /* 鸟类睡眠行为 */ }
    virtual void fly() { /* 鸟类飞行行为 */ }
};

class Sparrow : public Bird
{ // Sparrow is-a Bird
public:
    void eat() override { /* 麻雀进食行为 */ }
    void sleep() override { /* 麻雀睡眠行为 */ }
    void fly() override { /* 麻雀飞行行为 */ }
};
// 这个例子中，继承关系正确地表达了"is-a"语义：
//      麻雀是一种鸟
//      鸟是一种动物

// 常见的误用案例
// 1. 企鹅问题
class Penguin : public Bird
{ // 问题：企鹅不会飞
public:
    void fly() override
    {
        // 怎么实现？企鹅不能飞！
        throw std::logic_error("Penguins can't fly!");
    }
};
// 这违反了"is-a"关系，因为企鹅虽然是鸟，但不满足"所有鸟都能飞"的假设。

// 2. 矩形-正方形问题
class Rectangle
{
public:
    virtual void setWidth(int w) { width = w; }
    virtual void setHeight(int h) { height = h; }
    int getWidth() const { return width; }
    int getHeight() const { return height; }
    int area() const { return width * height; }

protected:
    int width;
    int height;
};

class Square : public Rectangle
{ // 问题：正方形有额外约束
public:
    void setWidth(int w) override
    {
        width = height = w; // 保持正方形特性
    }

    void setHeight(int h) override
    {
        width = height = h; // 保持正方形特性
    }
};
// 这看似合理（正方形是一种矩形），但会导致违反里氏替换原则：
void manipulateRectangle(Rectangle &r)
{
    r.setWidth(5);
    r.setHeight(4);
    assert(r.area() == 20); // 对于Square会失败！
}
Square s;
manipulateRectangle(s); // 断言失败！

// 正确设计的替代方案
// 1. 重新设计类层次
// 对于企鹅问题：
class Animal
{ /* ... */
};

class Bird : public Animal
{
    // 基本鸟类行为，不包括飞行
};

class FlyingBird : public Bird
{
    virtual void fly() = 0;
};

class NonFlyingBird : public Bird
{
    // 不会飞的鸟
};

class Sparrow : public FlyingBird
{ /* ... */
};
class Penguin : public NonFlyingBird
{ /* ... */
};

// 2. 使用组合而非继承
// 对于矩形-正方形问题：
class Shape
{
public:
    virtual int area() const = 0;
};

class Rectangle : public Shape
{
public:
    Rectangle(int w, int h) : width(w), height(h) {}

    void setWidth(int w) { width = w; }
    void setHeight(int h) { height = h; }
    int getWidth() const { return width; }
    int getHeight() const { return height; }

    int area() const override { return width * height; }

private:
    int width;
    int height;
};

class Square : public Shape
{
public:
    Square(int side) : sideLength(side) {}

    void setSide(int side) { sideLength = side; }
    int getSide() const { return sideLength; }

    int area() const override { return sideLength * sideLength; }

private:
    int sideLength;
};
// 识别is-a关系的测试
// 如何判断是否应该使用public继承？可以用以下测试：
// 1.替换测试：在任何使用基类的地方，都可以无缝地使用派生类吗？
// 2.行为测试：派生类是否保持了基类的所有行为语义？
// 3.常识测试：在自然语言中，"B是一种A"的说法是否合理？

// 实际应用中的考虑因素
// 1. 领域特定语义
// "is-a"关系可能依赖于特定领域的语义：
// 在生物学领域
class Tomato : public Fruit
{ /* ... */
}; // 正确：西红柿是水果

// 在烹饪领域
class Tomato : public Vegetable
{ /* ... */
}; // 也正确：烹饪中西红柿被视为蔬菜

// 2. 接口继承vs实现继承
// public继承同时继承了接口和实现，这可能导致问题：
class Bird
{
public:
    virtual void fly(); // 提供默认实现
};

class Ostrich : public Bird
{
    // 忘记重写fly()，导致鸵鸟使用了默认的飞行实现！
};

// 更好的设计是分离接口和实现：
class Bird
{
public:
    virtual void fly() = 0; // 纯虚函数，只定义接口
};

class FlyingBird : public Bird
{
protected:
    void defaultFly() { /* 默认飞行实现 */ } // 实现细节

public:
    void fly() override { defaultFly(); } // 使用默认实现
};

class Ostrich : public Bird
{
public:
    void fly() override { throw std::logic_error("Can't fly"); }
};

// 3. 私有继承和保护继承
// 当不是"is-a"关系时，可以考虑其他继承方式：
//      私有继承：通常表示"is-implemented-in-terms-of"（用...实现）关系
//      保护继承：介于public和private之间，允许派生类访问基类，但对外部隐藏
// 实际案例：STL中的继承关系
// STL容器之间没有继承关系，尽管它们有相似的接口：
// 错误设计
class Container
{ /* ... */
};
class Vector : public Container
{ /* ... */
};
class Deque : public Container
{ /* ... */
};
class List : public Container
{ /* ... */
};

// STL的实际设计
class Vector
{ /* ... */
}; // 没有继承关系
class Deque
{ /* ... */
};
class List
{ /* ... */
};

// 这是因为尽管它们都是容器，但它们之间不存在真正的"is-a"关系，它们的行为和性能特性有根本差异。

// 总结
// 条款32的核心教训是：
// 1.public继承表示is-a关系：派生类必须是基类的一种特例
// 2.is-a关系是关于行为的：不仅是数据，更是关于对象的行为
// 3.违反is-a关系会导致问题：代码可能在技术上正确，但在语义上错误
// 4.替代方案：当不是is-a关系时，考虑组合、私有继承或重新设计类层次
// 在设计继承关系时，应该始终问自己："这真的是一种is-a关系吗？"如果答案不是明确的"是"，那么应该重新考虑设计。